home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / System / scsi driver ƒ / Driver.c next >
Encoding:
C/C++ Source or Header  |  1989-04-17  |  19.0 KB  |  546 lines  |  [TEXT/KAHL]

  1. /*
  2. File Driver.c, the SCSI Hard Disk Driver
  3. Leo Drizis, March 1989
  4. Only for personal use, commercial use prohibited !!!
  5.  
  6. Compile this file with THINK'S Lightspeed C
  7. and link it with the <MacTraps> library
  8. */
  9.  
  10. #include <DiskDvr.h>
  11. #include <DeviceMgr.h>
  12. #include <MemoryMgr.h>
  13. #include <SCSIMgr.h>
  14. #include <FileMgr.h>
  15.  
  16. #define    DRVRSIZE    4000                /* an estimate for the driver length */
  17. #define BLKSIZE    512                                /* the sector size of our disk */
  18. #define SELECTERR    -2
  19. #define    SENDCMDERR    -3
  20. #define    COMPLERR    -4
  21. #define SENSEERR    -7                                /* define some error codes */
  22. #define MAXDRIVES    7                            /* max number of partitions */
  23.  
  24. #include "SCSITypes.h"                        /* SCSI commands & data structures */
  25.  
  26. extern DrvQElPtr DrvQHdrHead : 0x30A; /* start of the installed drives' queue */
  27. extern Ptr ScrnBase : 0x824;        /* start of the screen bitmap in memory */
  28.  
  29. #include "Driver.prot"        /* delete this line if you don't use prototypes */
  30.  
  31. scsiCmd    parkCmd = {0x1B,0,0,1,0};                        /* park heads command */
  32. scsiCmd    currCmd;                                /* current read/write command */
  33. scsiCmd    reqSenCmd = {3,0,0,0,0};                    /* request sense command */
  34. unsigned long    defSct;                        /* the last defect sector detected */
  35. char    isBlind;                            /* if we use blind reads/writes */
  36. tib    tib1,tib2,tib3;                                        /* tibs for read/write */
  37. Boolean    isShDwInst;                        /* if park heads on shutdown installed */
  38. unsigned int currID,                            /* the SCSI-ID for this driver */
  39.     firstDrive,                        /* the drive number of the 1st partition */
  40.     nDrives;                                /* the number of found partitions */
  41. myStruct drStats[MAXDRIVES];        /* status parameters for each partition */
  42.  
  43.  
  44. Jumper()        /* jumps to our main(), so that we can include the A4 routines */
  45. {
  46. asm { JMP main }
  47. }
  48.  
  49.  
  50. #include <SetUpA4.h>    /* lightspeed C file to adjust for access of globals */
  51.  
  52.  
  53. main()
  54. {
  55. THz    currZon;                                        /* the current memory zone */
  56. register    unsigned long ID;            /* the ID number for this SCSI device */
  57. register    unsigned int    DrRefNum;            /* reference no of our driver */
  58. long    err;
  59. register    DCtlPtr    DrEntry;                /* entry of driver into device map */
  60. register    Ptr    DrStart;                        /* start location of driver */
  61.  
  62.  
  63. goto start;                                            /* skip the driver header */
  64. asm {
  65. @DrvrHead:            /* this is the driver header. normal (non-SCSI) drivers */
  66.                     /* should start here */
  67.     DC.W    0x6F00                                    ; Flags 0110 1111 0000 0000
  68.     DC.W    0x0000                                    ; Delay
  69.     DC.W    0x0000                                    ; Mask
  70.     DC.W    0x0000                                    ; Menu
  71.     DC.W    @Open+8                                /* points to our open routine */
  72.     DC.W    @Prime+10                            /* points to our prime routine */
  73.     DC.W    @Control+12                        /* points to our control routine */
  74.     DC.W    @Status+14                        /* points to our status routine */
  75.     DC.W    @Close+16                            /* points to our close routine */
  76.     DC.B    9,'.','B','l','a','s','t','e','r',' '                ; Driver name
  77.                 /* the "glue" routines follow. they push A0 and A1 into the stack
  78.                     so that their C "brothers" can access them correctly */
  79. @Open                                                /* does really nothing ! */
  80.     MOVEQ    #0,D0
  81.     MOVE    D0,16(A0) ; ioResult
  82.     BRA.S    @end
  83. @Close /* 
  84.     MOVE.L    A0,-(A7)                                        /* push A0 & A1 */
  85.     MOVE.L    A1,-(A7)
  86.     JSR    Close1                                                /* call C routine */
  87.     BRA.S    @clrStack
  88. @Control
  89.     MOVE.L    A0,-(A7)                                        /* push A0 & A1 */
  90.     MOVE.L    A1,-(A7)
  91.     JSR    Control1                                            /* call C routine */
  92.     BRA.S    @clrStack
  93. @Status
  94.     MOVE.L    A0,-(A7)                                        /* push A0 & A1 */
  95.     MOVE.L    A1,-(A7)
  96.     JSR    Status1                                                /* call C routine */
  97.     BRA.S    @clrStack
  98. @Prime
  99.     MOVE.L    A0,-(A7)                                        /* push A0 & A1 */
  100.     MOVE.L    A1,-(A7)
  101.     JSR    Prime1                                                /* call C routine */
  102. @clrStack
  103.     MOVE.L (A7)+,A1                                /* remove A0 & A1 from stack */
  104.     MOVE.L     (A7)+,A0
  105. @end
  106.     MOVE    D0,0x142                            ; store error code in DskErr
  107.     BTST    #9,6(A0)                    ;    examine if immediate call in ioTrap
  108.     BNE.S    @exit
  109.     MOVE.L    0x8FC,-(A7) ; jIoDone
  110. @exit
  111.     RTS
  112.     }
  113.  
  114. start:
  115. asm { move.l    D5,ID }                /* on entry to the driver, D5 contains the
  116.                     SCSI-ID number of the drive in use. NOWHERE DOCUMENTED! */
  117. SetPtrSize(Jumper,DRVRSIZE);                        /* lock driver in memory */
  118.  if (ID >= 0) {
  119.     currZon = TheZone;                                    /* save current zone */
  120.     SetApplBase(SysZone->bkLim + DRVRSIZE);            /* expand the system zone */
  121.     TheZone = currZon;                                        /* restore zone */
  122.     }
  123. DrRefNum = ~( (ID & 0xFFFF) +32);        /* calculate reference number of driver
  124.                                                         from SCSI-ID number */
  125. asm {
  126.     MOVEQ    #0,D0
  127.     MOVE.W    DrRefNum,D0
  128.     DC.W    0xA03D        /* this is the _DrvrInstall trap. it installs a device
  129.        driver in the system. the driver ref-num is in D0. NOWHERE DOCUMENTED! */
  130.     MOVEQ    #0,D0
  131.     MOVE.W    ID,D0
  132.     ADD.L    #32,D0
  133.     ASL    #2,D0                            ; calculate offset to our driver's entry
  134.     MOVEA.L 0x11C,DrEntry ; uTableBase
  135.     MOVEA.L 0(DrEntry,D0.W),A0                            ; get our driver's entry
  136.     MOVEA.L (A0),DrEntry
  137.     _HLock    ; (A0/h:Handle)                                ; lock driver in memory
  138.     LEA    @DrvrHead,DrStart                            ; get our driver's header
  139.     MOVE.L    DrStart,(DrEntry)                    ; install driver's parameters
  140.     MOVE    (DrStart)+,4(DrEntry) ; dCtlFlags
  141.     MOVE.L    (DrStart)+,34(DrEntry) ; dCtlDelay & dCtlEmask
  142.     MOVE    (DrStart)+,38(DrEntry) ; dCtlMenu
  143.     BSET    #5,5(DrEntry)            ; declare that driver is open (dCtlFlags+1)
  144.     BCLR    #6,5(DrEntry)        ; declare that driver is ROM based (dCtlFlags+1)
  145.     LEA    Jumper,A0                            ; get starting address of our code
  146.     }                                /* end of the horrible assembler part ! */
  147. RememberA0(); /* save starting address of our code (needed to access globals) */
  148. err = myInstall(DrEntry);                                /* install filesystem */
  149. if (err != 0) return(-23);                                        /* Open error */
  150. else return(0);
  151. }
  152.  
  153.  
  154. myInstall(DCE)                                            /* install filesystem */
  155. DCtlPtr DCE;                            /* Data Control Entry to our driver */
  156. {
  157. register long i;
  158. register Boolean FSFound;                /* if we found a filesystem partition */
  159. DrvQElPtr D0;
  160. register int drvNum;                                /* current drive number */
  161. Partition prtt;                                        /* partition description */
  162. Boolean drvrFound;                            /* if we found a driver partition */
  163. int pMapLen;                                        /* length of partition map */
  164. register int err;
  165. register myStruct *currDr;                /* pointer to current partitions' data */
  166.  
  167. SetUpA4();                    /* adjust A4 to access global variables correctly */
  168. isShDwInst = FALSE;
  169. currID = ~DCE->dCtlRefNum - 32;                    /* find which SCSI-ID we have */
  170.     /* set up our tibs */
  171. tib1.cmd = scInc;                            /* read/write and increase pointer */
  172. tib1.cnt = BLKSIZE;                            /* how many bytes to read/write */
  173. tib2.cmd = scLoop;        /* loop to previous tib, the no of loops is set later */
  174. tib2.add = (Ptr)-10;
  175. tib3.cmd = scStop;                                    /* stop reading/writing  */
  176. FSFound = FALSE;                                    /* set up some variables */
  177. drvrFound = FALSE;
  178. pMapLen = 1;
  179. nDrives = 0;
  180. isBlind = TRUE;
  181. for (i=1; i<=pMapLen && nDrives<MAXDRIVES; i++) {
  182.                                     /* for all entries in the partition map */
  183.     err = rwBlock(&prtt,i,1,FALSE,currID);            /* read the p-map entry */
  184.     if (err != 0 && err != COMPLERR) continue;                /* skip if error */
  185.     if (prtt.pmSig != 0x504D) continue;            /* skip if no valid partition */
  186.     if ((prtt.pmPartStatus & 0x0010) == 0) continue;    /* partition hidden */
  187.     if (i == 1) pMapLen = prtt.pmMapBlkCnt;        /* get the real p-map length */
  188.     if (drvrFound == FALSE &&
  189.             strcmp(prtt.pmParType,"Apple_Driver") == 0 ) {
  190.         drvrFound = TRUE;                        /* we found a driver partition */
  191.         continue;
  192.         }
  193.     if (strcmp(prtt.pmParType,"Apple_HFS") == 0) {
  194.                                             /* we found a filesystem partition */
  195.         drvNum=8;                                /* SCSI-drives start from no 8 */
  196. NextD:    D0 = DrvQHdrHead;    /* search the drive queue to find a free drive no */
  197.         while (D0 != 0) {
  198.             if (D0->dQDrive == drvNum) {
  199.                 drvNum++;
  200.                 goto NextD;
  201.                 }
  202.             else D0 = D0->qLink;
  203.             }
  204.         currDr = &drStats[nDrives];        /* get start address of this partition's
  205.             status variables. avoids the very slow continuous array operations */
  206.         currDr->myStats.track = 0;
  207.         if (prtt.pmPartStatus & 0x0020) currDr->myStats.writeProt = 0;
  208.         else currDr->myStats.writeProt = 0x80;            /* if write-protected */
  209.         currDr->myStats.diskInPlace = 8;    /* show that this is a hard disk */
  210.         currDr->myStats.installed = 1;
  211.         currDr->myStats.sides = 0;
  212.         currDr->myStats.qType = 1;
  213.         currDr->myStats.qLink = 0;
  214.         currDr->myStats.dQDrive = drvNum;                    /* drive number */
  215.         currDr->myStats.dQRefNum = DCE->dCtlRefNum;            /* driver ref-num */
  216.         currDr->myStats.dQFSID = 0;                        /* apple's filesystem */
  217.         currDr->myStats.driveSize = prtt.pmDataCnt & 0xFFFF;
  218.         currDr->myStats.driveS1 = prtt.pmDataCnt >> 16;        /* no of sectors */
  219.         currDr->myStats.driveManf = 0;
  220.         currDr->myStats.driveChar = 0;
  221.         currDr->myStats.driveMisc = 0;
  222.         currDr->dataStart = prtt.pmPyPartStart;    /* first sector of partition */
  223.         currDr->dataCnt = prtt.pmDataCnt;        /* no of sectors of partition */
  224.         currDr->myStats.driveType = drvNum;                    /* drive number */
  225.         AddDrive(DCE->dCtlRefNum,drvNum,&currDr->myStats.qLink);
  226.                 /* declare that a new drive is connected. NOWHERE DOCUMENTED! */
  227.         if (FSFound == FALSE) firstDrive = drvNum;    /* remember the 1st drive */
  228.         nDrives++;                        /* one more filesystem partition found */
  229.         FSFound = TRUE;
  230.         }
  231.     }
  232. if (drvrFound == FALSE) {                                /* handle some errors */
  233.     RestoreA4();
  234.     return(-2);
  235.     }
  236. if (FSFound == FALSE) {                /* no filesystem isn't really an error! */
  237.     RestoreA4();
  238.     return(0);
  239.     }
  240. RestoreA4();                                    /* restore A4 to its old value */
  241. return(0);
  242. }
  243.  
  244.  
  245. Close1(DCE,PBP)                                            /* close our driver */
  246. ParmBlkPtr PBP;                                /* a read/write parameter block */
  247. DCtlPtr DCE;                            /* Data Control Entry to our driver */
  248. {
  249. int err,dr;
  250. register myStruct *currDr;                /* pointer to current partitions' data */
  251.  
  252. SetUpA4();                    /* adjust A4 to access global variables correctly */
  253. for (dr=0; dr<nDrives; dr++) {
  254.     err = Dequeue( drStats[dr].myStats.qLink, &DrvQHdr);
  255.     }
  256. if (isShDwInst == TRUE) ShutDwnRemove(ParkDrive);
  257. RestoreA4();                                    /* restore A4 to its old value */
  258. }
  259.  
  260.  
  261. Prime1(DCE,PBP)                                        /* read/write operations */
  262. ParmBlkPtr PBP;                                /* a read/write parameter block */
  263. DCtlPtr DCE;                            /* Data Control Entry to our driver */
  264. {
  265. Boolean isWrite;                                    /* if it is a write call */
  266. register unsigned long posit,nSect;
  267. register int err;
  268. register long lastDefect;
  269. register int retries;
  270. register myStruct *currDr;                /* pointer to current partitions' data */
  271.  
  272. SetUpA4();                    /* adjust A4 to access global variables correctly */
  273. currDr = &drStats[PBP->ioParam.ioVRefNum - firstDrive];    /* which partition ? */
  274. isWrite = ((PBP->ioParam.ioTrap & 0x00FF) == 3);    /* is it read or write ? */
  275. posit = DCE->dCtlPosition >> 9;                /* starting sector for read/write */
  276. nSect = PBP->ioParam.ioReqCount >> 9;                        /* no of sectors */
  277. if (PBP->ioParam.ioVRefNum != currDr->myStats.driveType) {
  278.     RestoreA4();
  279.     return(-56);                                            /* No such drive ! */
  280.     }
  281. if ((PBP->ioParam.ioReqCount & 0x1FF) != 0 &&
  282.         currDr->dataStart != 0 &&
  283.         currDr->dataCnt < nSect + posit) {
  284.     RestoreA4();
  285.     return(-50);                        /* Param list error, no such sector ! */
  286.     }
  287. if ((PBP->ioParam.ioPosMode & 0x40) != 0 && isWrite == FALSE) err = 0;
  288.                                         /* if we don't need to verify writes */
  289.     else {
  290.         for (retries=0, lastDefect=-1; retries < 4; ) {
  291.             err = rwBlock(PBP->ioParam.ioBuffer,currDr->dataStart+posit,
  292.                         nSect,isWrite,currID);        /* read/write the sectors */
  293.             if (err == 0) goto OK;                                /* successful */
  294.             if (lastDefect == defSct) retries++;            /* the same defect */
  295.             else {
  296.                 retries = 0;
  297.                 lastDefect = defSct;   /* another defect, give it 4 more tries */
  298.                 }
  299.             }                                    /* try 4 times to read/write */
  300.         PBP->ioParam.ioActCount = 0;                /* no sectors read/written */
  301.         RestoreA4();
  302.         return(-36);                                            /* I/O error */
  303.         }
  304. OK:                                                    /* operation successful */
  305. PBP->ioParam.ioActCount = PBP->ioParam.ioReqCount;    /* how many sectors r/w */
  306. DCE->dCtlPosition += PBP->ioParam.ioReqCount;                /* adjust position */
  307. RestoreA4();                                    /* restore A4 to its old value */
  308. return(err);
  309. }
  310.  
  311.  
  312. Control1(DCE,PBP)                                        /* control operations */
  313. ParmBlkPtr PBP;                                /* a read/write parameter block */
  314. DCtlPtr DCE;                            /* Data Control Entry to our driver */
  315. {
  316. int res;
  317. register int sw,dr;
  318. Ptr icnPtr;                                            /* pointer to disk icon */
  319. register myStruct *currDr;                /* pointer to current partitions' data */
  320.  
  321. SetUpA4();                    /* adjust A4 to access global variables correctly */
  322. res = 0;
  323. currDr = &drStats[PBP->ioParam.ioVRefNum - firstDrive];    /* which partition ? */
  324. if (PBP->cntrlParam.csCode == 65) {                    /* make periodic action */
  325.     if (isShDwInst == FALSE &&
  326.             GetTrapAddress(0x95) != GetTrapAddress(0x9F)) {
  327.                                         /* check if ShutDown is UnImplemented */
  328.         ShutDwnInstall(ParkDrive,sdOnPowerOff);        /* install ShutDown Proc */
  329.         isShDwInst = TRUE;
  330.         }
  331.     for (dr=0; dr<nDrives; dr++)            /* "insert" our partitions' disks */
  332.         res = PostEvent(7,drStats[dr].myStats.driveType);
  333.     DCE->dCtlFlags &= 0x4FFF;            /* at intr level,no goodbye,no time */
  334.     goto exit;
  335.     }
  336. if (currDr->myStats.driveType != PBP->ioParam.ioVRefNum) {
  337.     res = -56;                                                /* no such drive */
  338.     goto exit;
  339.     }
  340. sw = PBP->cntrlParam.csCode;
  341. switch (sw) {
  342.     case 5:                        /* verify disk in place (is always in place!) */
  343.         break;
  344.     case 6:                                                            /* format */
  345.         break;                        /* the driver must NOT format the drive ! */
  346.     case 21:
  347.     case 22:                                            /* return ICN# + data */
  348.         icnPtr = GetICN();                                /* get icon pointer */
  349.         *(icnPtr + 256 + 6) = currID + '0';                /* set our SCSI number */
  350.         BlockMove(&icnPtr,PBP->cntrlParam.csParam,4);
  351.         break;
  352.     case 7:    /* eject */
  353.         res = PostEvent(7,currDr->myStats.driveType);
  354.         res = -17;            /* cannot eject, unimplemented control instruction */
  355.         break;
  356.     default:
  357.         res = -17;                                        /* unimpl contr instr */
  358.         break;
  359.     }
  360. exit:
  361. RestoreA4();                                    /* restore A4 to its old value */
  362. return(res);
  363. }
  364.  
  365.  
  366. Status1(DCE,PBP)                                /* status of partition/drive */
  367. ParmBlkPtr PBP;                                /* a read/write parameter block */
  368. DCtlPtr DCE;                            /* Data Control Entry to our driver */
  369. {
  370. register int res;
  371. register myStruct *currDr;                /* pointer to current partitions' data */
  372.  
  373. SetUpA4();                    /* adjust A4 to access global variables correctly */
  374. res = 0;
  375. currDr = &drStats[PBP->ioParam.ioVRefNum - firstDrive];    /* which partition ? */
  376. if (currDr->myStats.driveType != PBP->ioParam.ioVRefNum) {
  377.     res = -56;                                                /* no such drive */
  378.     goto exit;
  379.     }
  380. if (PBP->cntrlParam.csCode == 8) {                            /* drive status */
  381.     BlockMove(&currDr->myStats,PBP->cntrlParam.csParam,22);
  382.     goto exit;                                /* return our status parameters */
  383.     }
  384. res = -18;                                        /* cannot respond to this call */
  385. exit:
  386. RestoreA4();                                    /* restore A4 to its old value */
  387. return(res);
  388. }
  389.  
  390.  
  391. void ParkDrive()                        /* is automatically called on shutdown */
  392. {
  393. SetUpA4();                    /* adjust A4 to access global variables correctly */
  394. mySCSI(900L,FALSE,FALSE,(Ptr)0L,currID,6,&parkCmd);                /* park heads */
  395. RestoreA4();                                    /* restore A4 to its old value */
  396. }
  397.  
  398.  
  399. rwBlock(buffer,LBA,nBlk,isWrite,id)                    /* reads/writes sectors */
  400. Ptr buffer;                                                /* where is the data ? */
  401. register unsigned long LBA;                                    /* starting sector */
  402. register unsigned int nBlk;                                /* how many sectors */
  403. Boolean isWrite;                                            /* write or read ? */
  404. unsigned int id;                                                    /* SCSI ID */
  405. {
  406. register unsigned int actNBlk;                            /* internal read count */
  407. register int err;
  408. senseBytes senseByt;
  409.  
  410. if (isWrite) currCmd.opCode = 0xA;                    /* write command opcode */
  411. else currCmd.opCode = 0x8;                                            /* read */
  412. tib1.add = buffer;                                    /* set r/w address in tib */
  413. while (nBlk > 0) {                                /* for all sectors requested */
  414.     currCmd.LUN = LBA >> 16;
  415.     currCmd.LBA = LBA;
  416.     if (nBlk > 256) actNBlk = 256;    /* cannot more than 256 sectors at once */
  417.     else actNBlk = nBlk;
  418.     tib2.cnt = actNBlk;                            /* set no of sectors in tib */
  419.     currCmd.lng = actNBlk;
  420.     err = mySCSI(180*60L,isBlind,isWrite,&tib1,id,6,&currCmd);/* send command */
  421.     if (err != 0) {
  422.         if (err != 2) return(err);
  423.         else {                                    /* we must ask for sense info */
  424.             err = reqSense(&senseByt,8);
  425.             if (err != 0) return(SENSEERR);
  426.             else {
  427.                 defSct = senseByt.LBA;                /* defect sector number */
  428.                 return(senseByt.senseKey);
  429.                 }
  430.             }
  431.         }
  432.     else {
  433.         nBlk -= actNBlk;                        /* adjust for next read/write */
  434.         LBA += actNBlk;
  435.         }
  436.     } /* while */
  437. return(err);
  438. }
  439.  
  440.  
  441. mySCSI(time,isBli,isWrite,tibPtr,id,cmdLen,cmdPtr)    /* send an SCSI command */
  442. unsigned long time;                                    /* how long we must wait */
  443. Boolean isBli,isWrite;
  444. Ptr tibPtr;                    /* pointer to our tib, NULL if no data transfer */
  445. unsigned int cmdLen,id;                    /* length of command, SCSI ID number */
  446. Ptr cmdPtr;                                                /* pointer to command */
  447. {
  448. register int res,err;
  449. register int try;                                            /* no of retries */
  450. int stat,messg;
  451.  
  452. res = 0;
  453. *ScrnBase ^= 0xC0;                            /* invert upper-left bit of screen */
  454. GetBus:                        /* try INDEFINITELY to get control of the SCSI-bus */
  455. for (try=0; try<4; try++) {
  456.     if (SCSIGet() == 0) goto GotIt;
  457.     }
  458. for (;;) if ((SCSIStat() & 0x0040) == 0) goto GetBus;            /* not busy */
  459. GotIt:                                                            /* got control */
  460. err = SCSISelect(id);                                    /* select the drive */
  461. if (err != 0) return(SELECTERR);
  462. for (try=0; try<4; try++) {                            /* try to send the command */
  463.     err = SCSICmd(cmdPtr,cmdLen);
  464.     if (err == 0) goto CmdOK;
  465.     }
  466. res = SENDCMDERR;
  467. goto Complete;
  468. CmdOK:                                        /* command sent, now transfer data */
  469. if (tibPtr != 0) {                                /* if there are data to send */
  470.     if (isBli) {                                            /* blind transfer */
  471.         if (isWrite) err = SCSIWBlind(tibPtr);
  472.         else err = SCSIRBlind(tibPtr);
  473.         }
  474.     else {                                                        /* non blind */
  475.         if (isWrite) err = SCSIWrite(tibPtr);
  476.         else err = SCSIRead(tibPtr);
  477.         }
  478.     res = err;
  479.     }
  480. Complete:                                    /* wait for command to complete */
  481. err = SCSIComplete(&stat,&messg,time);
  482. *ScrnBase ^= 0xC0;                            /* invert upper-left bit of screen */
  483. if (err != 0) return(COMPLERR);
  484. else {
  485.     if (res != 0 && stat == 2) return(stat);
  486.     return(res);
  487.     }
  488. }
  489.  
  490.  
  491. reqSense(buffer,count)    /* does a request sense to find which error occured */
  492. Ptr buffer;                                        /* where to store sense data */
  493. unsigned int count;                                /* how many data to request */
  494. {
  495. tib tib1,tib2;
  496. register int res;
  497.  
  498. reqSenCmd.lng = count;
  499. tib1.cmd = scNoInc;                            /* read and don't increase buffer */
  500. tib1.add = buffer;
  501. tib1.cnt = count;
  502. tib2.cmd = scStop;
  503. res = mySCSI(600L,FALSE,FALSE,&tib1,currID,6,&reqSenCmd);
  504. return(res);
  505. }
  506.  
  507.  
  508. Ptr GetICN()                                /* returns a pointer to disk icon */
  509. {
  510. register Ptr addr;
  511.  
  512. asm {
  513.     BRA    @doIt
  514. @icndata                                                /* this is the icon */
  515.     DC.W    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
  516.     DC.W    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
  517.     DC.W    0x0000,0x0000,0x000F,0xC000,0x0030,0x3000,0x00C0,0x0C00
  518.     DC.W    0x0100,0x0210,0x0200,0x0128,0x0200,0x01C4,0x0400,0x009A
  519.     DC.W    0x0407,0x8119,0x080F,0xC102,0x081F,0xE204,0x081F,0xE208
  520.     DC.W    0x081F,0xE430,0x081F,0xE4C0,0x080F,0xCB40,0x0407,0x9480
  521.     DC.W    0x0400,0x2880,0x0200,0x3100,0x0200,0x0100,0x0100,0x0200
  522.     DC.W    0x00C0,0x0C00,0x0030,0x3000,0x000F,0xC000,0x0000,0x0000
  523.                                                         ;  and this is the mask
  524.     DC.W    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
  525.     DC.W    0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000
  526.     DC.W    0x0000,0x0000,0x0000,0x0000,0x000F,0xC000,0x003F,0xF000
  527.     DC.W    0x00FF,0xFC00,0x01FF,0xFE10,0x01FF,0xFE38,0x03FF,0xFF64
  528.     DC.W    0x03F8,0x7EE6,0x07F0,0x3EFC,0x07E0,0x1DF8,0x07E0,0x1DF0
  529.     DC.W    0x07E0,0x1BC0,0x07E0,0x1B00,0x07F0,0x3480,0x03F8,0x6B00
  530.     DC.W    0x03FF,0xD700,0x01FF,0xCE00,0x01FF,0xFE00,0x00FF,0xFC00
  531.     DC.W    0x003F,0xF000,0x000F,0xC000,0x0000,0x0000,0x0000,0x0000
  532.                         ; this is the string in Finder's "At:" get info window
  533.     DC.B    6,'S','C','S','I',' ','0',0
  534. @doIt    LEA    @icndata,addr
  535.     }
  536. return(addr);
  537. }
  538.  
  539.  
  540. strcmp(s1, s2)                                            /* compare two strings */
  541. char *s1, *s2;
  542. {
  543. for (; *s1 == *s2 ; s1++, s2++) if (!*s1) break;
  544. return (*s1 - *s2);
  545. }
  546.